//
//  XSDschema.m
//  xsd2cocoa
//
//  Created by Stefan Winter on 5/22/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//


#import "XSDschema.h"
#import "CXMLElement.h"
#import "XSSimpleType.h"
#import "XSDcomplexType.h"

#import "MGTemplateEngine.h"
#import "ICUTemplateMatcher.h"
#import "XSSimpleType.h"

@implementation XSDschema

@synthesize targetNamespace = _targetNamespace;
@synthesize complexTypes;
@synthesize classPrefix = _classPrefix;
@synthesize classTemplateString;
@synthesize headerTemplateString;
@synthesize readComplexTypeElementTemplate;


- (void) loadTemplate: (NSURL*) templateUrl error: (NSError**) error{
    NSXMLDocument* xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL: templateUrl
                                                                 options:(NSXMLNodePreserveWhitespace|NSXMLNodePreserveCDATA)
                                                                   error: error];
    if(*error != nil) {
        return;
    }
    
    NSArray* simpleTypeNodes = [xmlDoc nodesForXPath:@"/template[1]/simpletype" error: error];
    if(*error != nil) {
        return;
    }
    
    for(NSXMLElement* aSimpleTypeNode in simpleTypeNodes) {
        XSSimpleType* aSimpleType = [[[XSSimpleType alloc] initWithElement: aSimpleTypeNode error: error] autorelease];
        [simpleTypeDict setValue: aSimpleType forKey: aSimpleType.name];
    }
    
    NSArray* nodes = [xmlDoc nodesForXPath:@"/template[1]/header" error: error];
    if(*error != nil) {
        return;
    }
    
    if(nodes != nil && nodes.count > 0) {
        self.headerTemplateString = [[nodes objectAtIndex: 0] stringValue];
    }    
    
    nodes = [xmlDoc nodesForXPath:@"/template[1]/class" error: error];
    if(*error != nil) {
        return;
    }
    
    if(nodes != nil && nodes.count > 0) {
        self.classTemplateString = [[nodes objectAtIndex: 0] stringValue];
    }
    
    nodes = [xmlDoc nodesForXPath:@"/template[1]/complextype[1]/read[1]/element[1]" error: error];
    if(*error != nil) {
        return;
    }
    
    if(nodes != nil && nodes.count > 0) {
        self.readComplexTypeElementTemplate = [[nodes objectAtIndex: 0] stringValue];
    }
}

- (void) generateInto: (NSURL*) destinationFolder
                error: (NSError**) error{
    
    for(XSDcomplexType* type in self.complexTypes) {
        if (self.headerTemplateString != nil) {
            NSString *result = [engine processTemplate: self.headerTemplateString withVariables: [type substitutionDict]];
            
            NSString* headerFileName = [NSString stringWithFormat: @"%@.h", [type targetClassName]];
            NSURL* headerFilePath = [destinationFolder URLByAppendingPathComponent: headerFileName]; 
            [result writeToURL: headerFilePath atomically:TRUE encoding: NSUTF8StringEncoding error: error];
            if(*error != nil) {
                return;
            }
        }
        
        if (self.classTemplateString != nil) {
            NSString *result = [engine processTemplate: self.classTemplateString withVariables: [type substitutionDict]];
            
            NSString* classFileName = [NSString stringWithFormat: @"%@.m", [type targetClassName]];
            NSURL* classFilePath = [destinationFolder URLByAppendingPathComponent: classFileName]; 
            [result writeToURL: classFilePath atomically:TRUE encoding: NSUTF8StringEncoding error: error];
            if(*error != nil) {
                return;
            }
        }
    }
}

- (id<XSType>) typeForName: (NSString*) qName {
    NSString* typeName = qName;
    NSArray* splitPrefix = [qName componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: @":"]];
    if(splitPrefix.count > 1) {
        typeName = (NSString*) [splitPrefix objectAtIndex: 1];
    }
    for(XSDcomplexType* type in self.complexTypes) {
        if([type.name isEqual: typeName]) {
            return type;
        }
    }
    return [simpleTypeDict objectForKey: typeName];;
}

// Called when initializing the object from a node
- (id) initWithNode: (CXMLElement*) node prefix: (NSString*) prefix error: (NSError**) error  {
	self = [self init];
    if(self) {
        // Set up template engine with your chosen matcher.
        engine = [[MGTemplateEngine templateEngine] retain];
        [engine setMatcher:[ICUTemplateMatcher matcherWithTemplateEngine:engine]];
        [engine setObject:@"Hi there!" forKey:@"hello"];
        
        simpleTypeDict = [[NSMutableDictionary dictionary] retain];
        
        self.targetNamespace = [[node attributeForName: @"targetNamespace"] stringValue];
        nsDeclDict = [[node nsDeclarations] retain];
        
        if(prefix != nil) {
            self.classPrefix = prefix;
        } else {
            for (NSString* anPrefix in [nsDeclDict allKeys]) {
                NSString* nsURI = [nsDeclDict objectForKey: anPrefix];

                if([nsURI isEqualTo: self.targetNamespace]) {
                    self.classPrefix = [anPrefix uppercaseString];
                }
            }
        }
        
        NSMutableArray* globalElements = [NSMutableArray array];
        self.complexTypes = [NSMutableArray array];
        // NSArray* ctNodes = [node nodesForXPath: @"/schema/complexType" error: error];
        for (CXMLElement* aChild in [node children]) {
            if([[[aChild name] lowercaseString] isEqual: @"complextype"]) {
                XSDcomplexType* aCT = [XSDcomplexType newWithNode: aChild schema: self];
                
                [((NSMutableArray*)self.complexTypes) addObject: aCT];
            } else if([[[aChild name] lowercaseString] isEqual: @"element"]) {
                XSDelement* anElement = [XSDelement newWithNode: aChild schema: self];
                [globalElements addObject: anElement];
            }
        }
        
        for (XSDelement* anElement in globalElements) {
            id<XSType> aType = [anElement schemaType];
            if( [aType isMemberOfClass: [XSDcomplexType class]]) {
                ((XSDcomplexType*)aType).globalElements = [((XSDcomplexType*)aType).globalElements arrayByAddingObject: anElement];
            }
        }
	}
	return self;
}

// Static method for initializing from a node.
+ (id) newWithNode: (CXMLElement*) node prefix: (NSString*) prefix error: (NSError**) error  {
    return (id)[[[XSDschema alloc] initWithNode: node prefix: prefix error: error] autorelease];
}

+ (id) newWithUrl: (NSURL*) schemaUrl prefix: (NSString*) prefix error: (NSError**) error {
    NSData* data = [NSData dataWithContentsOfURL: schemaUrl];
    CXMLDocument* doc = [[CXMLDocument alloc] initWithData: data options: 0 error: error];
    if(doc != nil) {
        return [XSDschema newWithNode: [doc rootElement] prefix: prefix error: error];
    }
    return nil;
}

- (void) addComplexType: (XSDcomplexType*) cType {
    [((NSMutableArray*)self.complexTypes) addObject: cType];
}

- (void)dealloc
{
    self.targetNamespace = nil;
    self.classPrefix = nil;
    self.complexTypes = nil;
    
    [nsDeclDict release];
    [simpleTypeDict release];
    [engine release];
    
    [super dealloc];
}

@end
